package api

import (
	sysContext "context"
	"errors"
	jsoniter "github.com/json-iterator/go"
	"take-a-break/app/controllers"
	"take-a-break/app/logic"
	"take-a-break/app/models"
	modelDb "take-a-break/app/models/db"
	modelRedis "take-a-break/app/models/redis"
	"take-a-break/app/models/request"
	"take-a-break/app/models/response"
	"take-a-break/component/redis"
	wechat_service "take-a-break/services/wechat"
)

type LoginController struct {
	controller.BaseController
	ctx sysContext.Context
}

//Prepare 预处理
func (c *LoginController) Prepare() {
	c.BaseController.Prepare()
	c.ctx = c.SysContextWithClass("[LoginCtl]")
}

// WxaLogin 小程序通过code进行登录并且注册
func (c *LoginController) WxaLogin() {
	req := new(request.WechatLoginReq)
	err := jsoniter.Unmarshal(c.Ctx.Input.RequestBody, &req)
	if len(req.Code) == 0 {
		c.TraceErrorResp(errors.New("获取code失败"))
		return
	}

	// 通过code2session 获取当前的openid, unionid, session_key
	res, err := wechat_service.NewMiniProgram(c.ctx).Code2Session(req.Code)
	if err != nil {
		c.TraceErrorResp(err)
		return
	}

	// 为了方便注册， 将code返回内容存储在redis中
	data := modelRedis.WxaCodeInfo{
		Code:       req.Code,
		OpenID:     res.OpenID,
		UnionID:    res.UnionID,
		SessionKey: res.SessionKey,
	}
	body, err := jsoniter.Marshal(data)
	if err != nil {
		c.TraceErrorResp(err)
	}
	_, _ = redis.Redis().SetString(modelRedis.GetWxaCodeKey(req.Code), string(body), 10800) // 缓存3小时

	// 检查是否已经存在账号, 存在则进行登录返回
	// 不存在则记录下当前授权信息, 并返回需要获取手机授权；授权后调用小程序注册方法
	// 检查条件:
	// 1. 存在openid的用户记录, 直接返回个人登录信息
	// 2. 存在unionid的用户记录, 将openid绑定到对应字段, 同时将通unionid的公众号openid一起绑定到对应字段
	// 3. 两者都不存在, 返回未注册或未绑定

	ip := c.Ctx.Input.IP()
	session := modelDb.GetMasterDbSession(c.ctx)
	token, user, err := logic.NewLoginLogic(c.ctx).LoginWithWxa(session, res.OpenID, res.UnionID, res.SessionKey, ip)
	if err != nil {
		c.TraceErrorResp(err)
		return
	}

	m := c.makeResponse(token, user)
	c.TraceSuccessResp(m)
	return
}

func (c *LoginController) makeResponse(token *models.TokenData, user *modelDb.User) *response.UserLoginRsp {
	m := &response.UserLoginRsp{
		LoginRspUserItem: response.LoginRspUserItem{
			AvatarURL:     user.AvatarURL,
			NickName:      user.NickName,
			Gender:        user.Gender,
			UserSid:       user.UserSid,
			RegTime:       user.RegTime,
			LastLoginTime: user.LastLoginTime,
			LastIP:        user.LastIP,
			Token:         token.Token,
			ExpireTime:    token.ExpireTime,
		},
	}
	return m
}

func (c *LoginController) WxaRegister() {
	jsCode := c.GetString("login_code")    // login 授权时的code
	phoneCode := c.GetString("phone_code") // 获取手机号时的code
	if len(phoneCode) == 0 {
		c.TraceErrorResp(errors.New("当前没有被授权"))
		return
	}

	// 通过code转换为手机号
	res, err := wechat_service.NewMiniProgram(c.ctx).GetUserPhoneNumber(phoneCode)
	if err != nil {
		c.TraceErrorResp(err)
		return
	}

	// 通过手机号查询当前账号是否存在, 存在则直接返回登录信息
	// 不存在则直接进行注册, 并返回登录信息
	phone := res.PurePhoneNumber
	ip := c.Ctx.Input.IP()
	exists, user, err := logic.NewLoginLogic(c.ctx).GetInfoWithPhone(phone) // 通过不加区号的手机号进行用户判断
	if err != nil {
		c.TraceErrorResp(err)
		return
	}
	session := modelDb.GetMasterDbSession(c.ctx)
	if !exists {
		// 未找到则进行注册
		name := phone[len(phone)-4:]
		user, err = logic.NewLoginLogic(c.ctx).CreateAccountDB(session, phone, "", name, ip)
		if err != nil {
			c.TraceErrorResp(err)
			return
		}
	}

	// 通过缓存获取code的信息
	body, err := redis.Redis().GetString(modelRedis.GetWxaCodeKey(jsCode))
	if err != nil && err.Error() != "redigo: nil returned" {
		c.TraceErrorResp(err)
		return
	}
	if len(body) == 0 {
		c.TraceErrorResp(errors.New("获取信息失败，请重新授权"))
		return
	}
	data := modelRedis.WxaCodeInfo{}
	err = jsoniter.UnmarshalFromString(body, &data)
	if err != nil {
		c.TraceErrorResp(err)
		return
	}

	// 将当前的openid等信息填充到用户表
	logicLogin := logic.NewLoginLogic(c.ctx)
	err = logicLogin.UpdateWxaInfo(session, user, data.OpenID, data.UnionID, data.SessionKey)
	if err != nil {
		c.TraceErrorResp(err)
		return
	}

	token, user, err := logicLogin.LoginWithWxa(session, data.OpenID, data.UnionID, data.SessionKey, ip)
	if err != nil {
		c.TraceErrorResp(err)
		return
	}

	m := c.makeResponse(token, user)
	c.TraceSuccessResp(m)
	return
}
